package monitorcon

import (
	//"encoding/json"
	//"fmt"
	//"github.com/astaxie/beego"

	//"gopkg.in/redis.v4" //redis
	"log"
	"strconv"
	"strings"
	//"time"
)

//InstanceManager class: 实例管理类
type InstanceManager struct {
	//..
	id int
}

//MLABEL : 实例节点开头字符
const MLABEL string = "M"

//TOKEN : 实例token
const TOKEN string = "token"

//HOSTNUM : 设备个数
const HOSTNUM string = "hostnum"

//NUMFACTOR : 设备上限数因子
const NUMFACTOR float32 = 0.1

//M1                        M2      M3
//  hostnum
//  tokennum
//  hid1:uuid,1 (可以采集)
//  hid2:uuid,0(不可以采集)

//func:分配id，monitor总数增长1
//hostnum=-1 unused conn,
//hostnum >=0 used conn

//Init :初始化函数
func (m *InstanceManager) Init(rs *RedisManager) int {
	log.Println("init start")

	livenum, err := rs.GetSinglefield(LIVECONNNUMLABEL)
	var inum int
	inum = -1
	if err != nil || livenum == "0" {
		log.Println("create first connection")

		//m.rsclient=rs
		maxconnum, err := rs.GetMaxconnum()
		if err != nil {
			maxconnum = MAXCONNUM
		}

		m.initconnection(maxconnum, rs)

		rs.SetSinglefield(LIVECONNNUMLABEL, "0") //first live connection

		inum = 1

	} else {

		log.Println("create new connection")
		//得到空闲的连接

		inum = m.getfreeconnection(rs)
		if inum == -1 { //没有空闲连接
			log.Println("all connection is already used")
			return -1
		}

	}

	//分配id
	m.id = inum

	log.Printf("instance id=%d", m.id)

	index := strconv.Itoa(inum)

	map1 := make(map[string]interface{})

	map1[TOKEN] = "0"
	rs.SetInstancefield(index, map1)

	map2 := make(map[string]interface{})

	map2[HOSTNUM] = "-1" //初始化
	rs.SetInstancefield(index, map2)

	m.updatelivecon(rs, "add") //livenum=livenum+1
	return inum
}

//initconnection ：初始化连接
func (m *InstanceManager) initconnection(maxnum int, rs *RedisManager) {

	var i int
	var connindex string
	for i = 1; i <= maxnum; i++ {

		connindex = strconv.Itoa(i)

		rs.DelInstancefield(connindex) //删除所有监控实例
		rs.Delinsdevicelist(connindex) //删除与实例关联的列表key
	}

	rs.DelSinglefield(DATAPERIODLABEL)          //删除数据周期key in redis
	rs.Deldevicelists(DEVICELISTKEY)            //删除设备列表key
	rs.Delsubscribelists(SUBSCRIBFIELDLIST)     //删除订阅列表
	rs.Delsubscribelists(SUBSCRIBFIELDITEMLIST) //删除订阅列表

}

//getfreeconnection ：得到空闲的连接
func (m *InstanceManager) getfreeconnection(rs *RedisManager) int {
	var i int
	var connindex string
	maxnum, err := rs.GetSinglefield(MAXCONNECTLABEL)
	if err != nil {
		log.Println(err)
		return -1
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return -1
	}

	for i = 1; i <= imaxnum; i++ {
		connindex = strconv.Itoa(i)
		flag := rs.Exists(connindex) //exists key return 0,else return 1

		if flag == 0 { //找到空闲连接
			break
		}
	}

	if i > imaxnum { //没有空闲连接
		//fmt.Println("all connection is used")
		//log.Println("all connection is used")
		i = -1
	}
	//log.Println("free connection index=", i)
	return i //return index
}

//live ：livenum增加或减少1
func (m *InstanceManager) updatelivecon(rs *RedisManager, op string) {
	var inum int
	num, err := rs.GetSinglefield(LIVECONNNUMLABEL)
	if err != nil {
		log.Println(err)
		return
	}
	inum, err = strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if op == "add" {
		inum = inum + 1
	} else {

		inum = inum - 1
	}

	liveconnumstr := strconv.Itoa(inum)
	rs.SetSinglefield(LIVECONNNUMLABEL, liveconnumstr) //first live num

}

//Updatetoken :使用定时器更新token ,采集数据
func (m *InstanceManager) Updatetoken(rs *RedisManager) {

	var i, tokennum int
	var connindex string
	maxnum, err := rs.GetSinglefield(MAXCONNECTLABEL)
	if err != nil {
		log.Println(err)
		return
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return
	}

	for i = 1; i <= imaxnum; i++ {

		connindex = strconv.Itoa(i) //MLABEL +

		tnum, err := rs.GetInstancefield(connindex, TOKEN) //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//log.Printf("%s key is no existed\n",connindex)
			continue
		}

		tokennum, err = strconv.Atoi(tnum)

		if err != nil {
			log.Println(err)
			return
		}

		if tokennum == -1 {
			continue //是空闲连接，无需update token
		}

		//log.Printf("index=%d\n",i)
		if m.id == i { //是自身连接, update token=0
			//need redis watch ,保证操作成功

			//m.setselfconnection(rs,connindex)
			map1 := make(map[string]interface{})
			map1[TOKEN] = "0"

			rs.SetInstancefield(connindex, map1)
			log.Printf("self M%s token=0\n", connindex)

			//采集数据,储存数据到redis

			getinterfacedata(i, rs, 0) //for 0 :redis

		} else { //update other connection token , token+1

			tokennum = tokennum + 1

			var livenum int

			num, err := rs.GetSinglefield(LIVECONNNUMLABEL) //得到当前live连接数
			if err != nil {
				log.Println(err)
				return
			}
			livenum, err = strconv.Atoi(num)

			var upperlimit float32
			upperlimit = float32(imaxnum) * NUMFACTOR
			upperlimit = upperlimit * float32(livenum) //上线= maxnum*factor*livenum
			log.Println("upper limit", upperlimit)
			if tokennum >= int(upperlimit) { //重置为空闲的连接

				map1 := make(map[string]interface{})
				map1[TOKEN] = "-1"
				rs.SetInstancefield(connindex, map1)

				/*
							map2 := make(map[string]interface{})
					        map2[HOSTNUM] = "-1"
							rs.SetInstancefield(connindex, map2)*/

				m.reallocate(i, rs)
				log.Printf("M%s connection is reset\n", connindex)
				m.updatelivecon(rs, "del") //live conn num -1
			} else {

				_, err := rs.GetInstancefield(connindex, TOKEN) //得到token ,如果token=-1,则是空闲的连接
				if err != nil {
					log.Printf("M%s token is no existed\n", connindex)
					continue
				}

				tokennumstr := strconv.Itoa(tokennum)
				map1 := make(map[string]interface{})
				map1[TOKEN] = tokennumstr
				rs.SetInstancefield(connindex, map1)

				//rs.HSet(connindex, TOKEN, tokennumstr)
				log.Printf("M%s token=%s\n", connindex, tokennumstr)

			}
		}

	}

	// use datamanage to save data to postgres

	datamanagementstart(rs)
}

//index: 连接设备的connection index
//reallocate :重新分配设备
func (m *InstanceManager) reallocate(index int, rs *RedisManager) {

	connindex := strconv.Itoa(index)                    //MLABEL +
	num, err := rs.GetInstancefield(connindex, HOSTNUM) //得到token ,如果token=-1,则是空闲的连接

	//num, err := rs.HGet(connindex, HOSTNUM).Result() //得到此实例的hostnum ,判断是否有需要重新分配的设备

	if err != nil {
		//fmt.Printf("%s token is no existed\n",connindex)
		return
	}

	hostnum, err := strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if hostnum == -1 {
		return //没有可分配的设备
	}

	var i int
	for i = hostnum; i >= 1; i-- {

		//hi := strconv.Itoa(i)

		//hostinfo, err := rs.HGet(connindex, "hid"+hi).Result()

		hostinfo, err := rs.GetValueFromList(connindex, i-1) //得到指定实例的设备内容
		if err != nil {

			continue
		}

		//s := strings.Split(hostinfo, ",")
		//log.Printf("%s,%s\n", s[0], s[1])
		rs.RemoveFromlist(connindex, hostinfo) // 移除表中所有与 VALUE 相等的值。
		//rs.HDel(connindex, "hid"+hi)   //删除hid节点
		m.RegisterHost(rs, hostinfo) //重新分配节点

	}

	rs.Delinsdevicelist(connindex) //删除废弃列表
	rs.DelInstancefield(connindex) //删除M1 ,M2 etc

}

//第一个hid1,以后依次为hid2,hid3，hidXXX
//首先分配hostnum=-1 的连接，以后会选择hostnum最小的连接

//RegisterHost :分配host  //hostuuid string, optype string
func (m *InstanceManager) RegisterHost(rs *RedisManager, hostinfo string) bool {

	var i int
	var connindex string

	//var sid = strconv.Itoa(m.id)
	//log.Println("mid=", sid)
	log.Println(" is registering device" + hostinfo)

	maxnum, err := rs.GetSinglefield(MAXCONNECTLABEL)
	if err != nil {
		log.Println(err)
		return false
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return false
	}

	var minhostnum, minindex int
	minhostnum = MAXCONNUM //初始化为最大值100
	for i = 1; i <= imaxnum; i++ {

		connindex = strconv.Itoa(i) //MLABEL +
		var flag int
		flag = rs.Exists(connindex) //exists key return 1,else return 0
		//log.Println("key flag", flag)
		if flag == 0 { //key 不存在
			continue
		}

		num, err := rs.GetInstancefield(connindex, TOKEN) //查看已经分配的hostnum

		//num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//log.Printf("%s token is no existed\n", connindex)
			continue
		}
		var tokennum int
		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			continue
		}

		if tokennum == -1 {
			continue //是空闲连接，不能分配host
		}

		hnum, err := rs.GetInstancefield(connindex, HOSTNUM) //查看已经分配的hostnum
		if err != nil {
			hnum = "-1" //初始化hostnum

		}

		hostnum, err := strconv.Atoi(hnum)
		if err != nil {

			continue
		}

		if hostnum == -1 { //选择成为最小的hostnum,分配当前的host

			minhostnum = 0
			minindex = i
			break //结束分配
		} else {
			if hostnum < minhostnum {
				minhostnum = hostnum
				minindex = i
			}
		}

	}

	if minhostnum != MAXCONNUM {
		connindex = strconv.Itoa(minindex) //MLABEL +
		//分配设备，minhostnum增加1

		rs.Appendtolist(connindex, hostinfo) //append value to list end
		//rs.LSet(connindex,int64(minhostnum),hostuuid+","+optype)
		minhostnum = minhostnum + 1
		mnum := strconv.Itoa(minhostnum)
		//log.Println("list len=", minhostnum)

		map1 := make(map[string]interface{})
		map1[HOSTNUM] = mnum
		rs.SetInstancefield(connindex, map1) //设置hostnum+1

		//rs.HSet(connindex, HOSTNUM, mnum)                   //控制的设备数目增长1
	}

	return true

}

//OpenHost :打开设备的数据采集
func (m *InstanceManager) OpenHost(rs *RedisManager, hostuuid string) bool {
	return OpHost(rs, hostuuid, "1")
}

//CloseHost :关闭设备的数据采集
func (m *InstanceManager) CloseHost(rs *RedisManager, hostuuid string) bool {
	return OpHost(rs, hostuuid, "0")
}

//OpHost :打开 or 关闭设备的数据采集
func OpHost(rs *RedisManager, hostuuid string, op string) bool {

	var i, j, tokennum int
	var connindex string //, hindex

	maxnum, err := rs.GetSinglefield(MAXCONNECTLABEL)
	if err != nil {
		log.Println(err)
		return false
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return false
	}

	//寻找与本设备关联的管理实例
	for i = 1; i <= imaxnum; i++ {

		connindex = strconv.Itoa(i)                       //MLABEL +
		num, err := rs.GetInstancefield(connindex, TOKEN) //查看已经分配的hostnum

		//num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//log.Printf("%s token is no existed\n", connindex)
			continue
		}

		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return false
		}

		if tokennum == -1 {
			continue //是空闲连接，不能分配host
		}

		if tokennum >= 0 { //正在使用的token
			hnum, err := rs.GetInstancefield(connindex, HOSTNUM) //查看已经分配的hostnum
			if err != nil {

				continue
			}

			hostnum, err := strconv.Atoi(hnum)
			if err != nil {

				continue
			}

			if hostnum == -1 {
				continue
			}

			for j = 1; j <= hostnum; j++ {

				//hindex = strconv.Itoa(j)

				//hostinfo, err := rs.HGet(connindex, "hid"+hindex).Result()
				hostinfo, err := rs.GetValueFromList(connindex, j-1)
				if err != nil {
					continue
				}

				s := strings.Split(hostinfo, ",")
				log.Printf("search device %s,%s\n", s[0], s[1])

				if s[0] == hostuuid { //定位到需要的hostuuid
					s[1] = op //重置操作, 1:open host,0:closehost
					//rs.HSet(connindex, "hid"+hindex, s[0]+","+s[1])
					rs.Setlistvalue(connindex, j-1, s[0]+","+s[1])
					return true
				}
			}

		}
	}

	return false //在redis内没有找到设备的hostuuid
}

//UnregisterHost 注销Host :注销设备
func (m *InstanceManager) UnregisterHost(rs *RedisManager, hostuuid string) bool {

	var i, j, tokennum int
	var connindex string //, hindex

	maxnum, err := rs.GetSinglefield(MAXCONNECTLABEL)
	if err != nil {
		log.Println(err)
		return false
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return false
	}

	//寻找与本设备关联的管理实例
	for i = 1; i <= imaxnum; i++ {

		connindex = strconv.Itoa(i)                       //MLABEL +
		num, err := rs.GetInstancefield(connindex, TOKEN) //查看已经分配的hostnum

		//num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//log.Printf("%s token is no existed\n", connindex)
			continue
		}

		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return false
		}

		if tokennum == -1 {
			continue //是空闲连接，不能分配host
		}

		if tokennum >= 0 { //正在使用的token
			hnum, err := rs.GetInstancefield(connindex, HOSTNUM) //查看已经分配的hostnum
			if err != nil {

				continue
			}

			hostnum, err := strconv.Atoi(hnum)
			if err != nil {

				continue
			}

			if hostnum == -1 {
				continue
			}

			for j = 1; j <= hostnum; j++ {

				//hindex = strconv.Itoa(j)

				//hostinfo, err := rs.HGet(connindex, "hid"+hindex).Result()
				hostinfo, err := rs.GetValueFromList(connindex, j-1)
				if err != nil {
					continue
				}

				s := strings.Split(hostinfo, ",")
				log.Printf("search device %s,%s\n", s[0], s[1])

				if s[0] == hostuuid { //定位到需要的hostuuid

					//rs.HSet(connindex, "hid"+hindex, s[0]+","+s[1])
					//删除本项
					rs.RemoveFromlist(connindex, s[0]+","+s[1])
					rs.DelSinglefield(s[0]) //删除设备本身的key

					hostnum = hostnum - 1
					map1 := make(map[string]interface{})
					map1[HOSTNUM] = hostnum
					rs.SetInstancefield(connindex, map1) //实例管理的设备数-1

					return true
				}
			}

		}
	}

	return false //在redis内没有找到设备的hostuuid
}

//Do :回调函数入口
func (m *InstanceManager) Do(hostuuid string, callback Callback) bool {
	return callback(hostuuid)
}
